package com.third.filter;

import com.third.entity.RestBean;
import com.third.utils.Const;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author 唐博文
 * @version 2023.1.2
 * @date 2023/10/23 22:16 星期一
 */
@Component
@Order(Const.ORDER_LIMIT)
//实现限流功能
public class FlowLimitFilter extends HttpFilter {
    
    @Resource
    StringRedisTemplate template;
    
    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String address=request.getRemoteAddr();
        if(this.tryCount(address)){
            chain.doFilter(request,response);
        }else{
            this.writeBlockMessage(response);
        }
        
    }
    private void writeBlockMessage(HttpServletResponse response) throws IOException {
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(RestBean.forbidden("操作频繁，请稍后再试").asJsonString());
    }
    
    private boolean tryCount(String ip){
        synchronized (ip.intern()){
            if(Boolean.TRUE.equals(template.hasKey(Const.FLOW_LIMIT_BLOCK + ip)))
                return false;
            return this.limitPeriodCheck(ip);
        }
    }
    
    private boolean limitPeriodCheck(String ip){
        //redis中是否有ip了
        if(Boolean.TRUE.equals(template.hasKey(Const.FLOW_LIMIT_COUNT + ip))) {
            Long increment= Optional.ofNullable(template.opsForValue().increment(Const.FLOW_LIMIT_COUNT+ip)).orElse(0L);
            if(increment > 10 ){
                template.opsForValue().set(Const.FLOW_LIMIT_BLOCK+ip,"",30, TimeUnit.SECONDS);
                return false;
            }
        }else {
            template.opsForValue().set(Const.FLOW_LIMIT_COUNT + ip, "1", 3, TimeUnit.SECONDS);
        }
        return true;
    }
}

